home *** CD-ROM | disk | FTP | other *** search
/ Cream of the Crop 3 / Cream of the Crop 3.iso / comm / spar372.zip / SPAR_FUN.C < prev    next >
C/C++ Source or Header  |  1994-01-18  |  18KB  |  575 lines

  1. /*
  2. ** $Source: dh1:network/parnet/Sana2/Sources/spar_funcs.c,v $
  3. ** $State: Exp $
  4. ** $Revision: 37.2 $
  5. ** $Date: 93/12/17 23:12:43 $
  6. ** $Author: S.A.Pechler $
  7. **
  8. ** Amiga SANA-II Example PARnet device driver.
  9. **
  10. ** SPAR code.
  11. **
  12. ** Based on the Amiga SANA-II Example SLIP device driver code by bj, 
  13. ** which is (C) Copyright 1992 Commodore-Amiga, Inc.
  14. ** the rhslip.device by Olaf Seibert <rhialto@mbfys.kun.nl>, and on
  15. ** the agnet.device code by ppessi <Pekka.Pessi@hut.fi>, which is
  16. ** Copyright (c) 1993 AmiTCP/IP Group,
  17. **                    Helsinki University of Technology, Finland.
  18. **                    All rights reserved.
  19. **
  20. */
  21.  
  22. #include "device_protos.h"
  23.  
  24. struct IntuitionBase *IntuitionBase=NULL;
  25.  
  26. extern int atoi(const char *); /* From stdlib.h */
  27.  
  28. #ifdef DEBUG
  29.  
  30. #include "spar_debug.h"
  31.  
  32. struct Window *DebWin1=NULL;
  33. struct IOStdReq *DebReq=NULL;
  34. struct MsgPort *DebPort=NULL;
  35.  
  36.  
  37. # ifdef __STDC__
  38. #  include <stdarg.h>
  39.  
  40.    extern int vsprintf(char *, const char *, va_list);    /* from stdio.h */
  41.  
  42.    void LogMessage(char *fmt, ...)
  43.    {
  44.      va_list args;
  45. # else
  46. #  include <varargs.h>
  47.  
  48.    void LogMessage(va_alist)
  49.      va_dcl
  50.    {
  51.      char *fmt;
  52.      va_list args;
  53. # endif
  54.  
  55.   char DebugText[128];
  56.  
  57. #ifdef __STDC__
  58.     va_start(args, fmt);
  59. #else
  60.     va_start(args);
  61.     fmt = va_arg(args, char *);
  62. #endif
  63.  
  64.    vsprintf(DebugText,fmt,args);
  65.  
  66.    if(DebReq && DebReq->io_Device)
  67.    {
  68.       DebReq->io_Command=CMD_WRITE;
  69.       DebReq->io_Data=(APTR)DebugText;
  70.       DebReq->io_Length=-1L;
  71.       DoIO((struct IORequest *)DebReq);
  72.    }
  73.  
  74.    va_end(args);
  75. }
  76.  
  77.  
  78. void initsyslog(void)
  79. {
  80.  
  81.    if(IntuitionBase = (struct IntuitionBase *)OpenLibrary("intuition.library",37L))
  82.    {
  83.      DebWin1=OpenWindow(&DebugWin);
  84.  
  85.      if(DebPort=CreateMsgPort())
  86.       if(DebReq=CreateIORequest(DebPort,sizeof(struct IOStdReq)))
  87.       {
  88.          DebReq->io_Data=(APTR) DebWin1;
  89.          DebReq->io_Length= sizeof(struct Window);
  90.          OpenDevice("console.device",0, (struct IORequest *)DebReq, 0);
  91.       }
  92.  
  93.    }
  94. }
  95.  
  96. void uninitsyslog(void)
  97. {
  98.  
  99.   AbortIO((struct IORequest *)DebReq);
  100.   WaitIO((struct IORequest *)DebReq);
  101.  
  102.   while(GetMsg(DebPort));
  103.  
  104.   CloseDevice((struct IORequest *)DebReq);
  105.   
  106.   if(DebReq) DeleteIORequest(DebReq);
  107.   if(DebPort) DeleteMsgPort(DebPort);
  108.  
  109.  
  110.   if (DebWin1) CloseWindow(DebWin1);
  111.   if (IntuitionBase) CloseLibrary((struct Library *)IntuitionBase);
  112. }
  113. #else /* No debugging, use intuition only for error messages */
  114.  
  115. void initintuition(void)
  116. {
  117.   IntuitionBase = (struct IntuitionBase *)OpenLibrary("intuition.library",37L);
  118. }
  119.  
  120. void uninitintuition(void)
  121. {
  122.   if (IntuitionBase) CloseLibrary((struct Library *)IntuitionBase);
  123. }
  124.  
  125. #endif /* DEBUG */
  126.  
  127.  
  128. /*
  129. **
  130. ** ReadConfig
  131. **
  132. ** Attempt to read in and parse the driver's configuration file.
  133. **
  134. ** The files are named by ENV:SANA2/sparX.config where X is the decimal
  135. ** representation of the device's unit number.
  136. **
  137. */
  138.  
  139. BOOL ReadConfig(struct SPARDevUnit *sdu)
  140. {
  141.     UBYTE *linebuff,conffile[40]; /* temporary linebuffer & filename */
  142.     STRPTR termchar;          /* line terminator */
  143.     struct RDArgs *rdargs;
  144.     BPTR ConfigFile;          /* DOS file desciptor */
  145.     LONG args[4];             /* arguments to read from the file */
  146.     BOOL status = FALSE;
  147.     ULONG linenum=0;          /* line counter in config file */
  148.     UWORD i;
  149.  
  150.     struct EasyStruct es;     /* For errormessage to user */
  151.  
  152.     /* initialize structure for a possible error message */
  153.     es.es_StructSize=sizeof(struct EasyStruct);
  154.     es.es_Flags=0;
  155.     es.es_Title=(char *)SPARName;
  156.     es.es_GadgetFormat="Okay";
  157.  
  158.     /* Create the name of our config file.. */
  159.     sprintf(conffile,"ENV:SANA2/spar%ld.config",(ULONG)sdu->sdu_UnitNum);
  160.  
  161.     /* ...and open it. */
  162.     if(ConfigFile = Open(conffile,MODE_OLDFILE))
  163.     {
  164.       /* Here, I use ReadArgs() to do the file parsing for me. */
  165.  
  166.       if(linebuff = AllocMem(256,MEMF_CLEAR|MEMF_PUBLIC))
  167.       {
  168.           if(rdargs = AllocDosObject(DOS_RDARGS, NULL))
  169.           {
  170.             while(FGets(ConfigFile, linebuff, 255))
  171.             {
  172.                 linenum++;
  173.  
  174.                 if(linebuff[0] == '#') /* Skip comment lines */
  175.                     continue;
  176.  
  177.                 rdargs->RDA_Source.CS_Buffer = linebuff;
  178.                 rdargs->RDA_Source.CS_Length = 256;
  179.                 rdargs->RDA_Source.CS_CurChr = 0;
  180.                 rdargs->RDA_DAList = NULL;  /* MUST be initalized to NULL */
  181.  
  182.                 /* ReadArgs() requires that the line be null-terminated
  183.                  * or funny things happen. */
  184.  
  185.                 termchar = (STRPTR) linebuff + strlen(linebuff);
  186.                 *termchar = '\n';
  187.                 termchar++;
  188.                 *termchar = 0;
  189.  
  190.                 for(i = 0; i< 3; i++) args[i]=0; /* clear argument list */
  191.  
  192.                 /* Parse the line...*/
  193.  
  194.                 /* I use 'DESTADDR' as a string, otherwise I can't check if
  195.                  * it's omitted. (when using 'DESTADDR/N' and you don't
  196.                  * fill in this argument in the config.file, args[3] will
  197.                  * still point to something.)
  198.                  */
  199.                 if(ReadArgs("PARNAME/A,PARUNIT/A/N,PARADDR/A/N,DESTADDR",args,rdargs))
  200.  
  201. /*              if(ReadArgs("PARNAME/A,PARUNIT/A/N,PARADDR/A/N,DESTADDR/N",args,rdargs))
  202. */
  203.                 {
  204.                   strcpy(sdu->sdu_ParDevName,(STRPTR)args[0]);
  205.                   sdu->sdu_ParUnitNum = *((ULONG *)args[1]);
  206.                   sdu->sdu_StAddr = sdu->sdu_HwAddr = (UBYTE) *((ULONG *)args[2]);
  207.  
  208.                   if(args[3]) sdu->sdu_DestAddr = (UBYTE) atoi((char *)args[3]);
  209.  
  210. /*                if(args[3]) sdu->sdu_DestAddr = (UBYTE) *((ULONG *)args[3]);
  211. */
  212.                   else sdu->sdu_DestAddr = (UBYTE) 0;
  213.  
  214.                   /* check hardware addresses */
  215.                   if ((sdu->sdu_HwAddr!=0) && (sdu->sdu_HwAddr!=255) &&
  216.                       (sdu->sdu_DestAddr!=255))
  217.                     status = TRUE;
  218.                   else
  219.                   {
  220.                     /* send a notification to the user */
  221.                     es.es_TextFormat="Invalid hardware address (%d) given.";
  222.                     EasyRequestArgs(NULL, &es, 0, &sdu->sdu_HwAddr);
  223.                   }
  224.                   debug(("Config: Hardware addr:%d, dest.addr:%d\n",(int)sdu->sdu_HwAddr,(int)sdu->sdu_DestAddr))
  225.  
  226.                   FreeArgs(rdargs);
  227.                   break;
  228.                 }
  229.                 else  /* Argument error */
  230.                 {
  231.                   /* Produce an error message in a requester */
  232.                   es.es_TextFormat="Error in configuration file on line %ld.";
  233.                   EasyRequestArgs(NULL, &es, 0, &linenum);
  234.                   break;
  235.                 }
  236.             }
  237.             FreeDosObject(DOS_RDARGS,rdargs);
  238.         }
  239.         FreeMem(linebuff, 256);
  240.       }
  241.       Close(ConfigFile);
  242.     }
  243.   return(status);
  244. }                 
  245.  
  246.  
  247. VOID SendPacket(struct SPARDevUnit *sdu, struct IOSana2Req *ios2)
  248. {
  249.     struct IOParReq *iopar;
  250.     struct BufferManagement *bm;
  251.     struct Spar_Hdr *PktHdr;  /* Spar frame header */
  252.  
  253.     bm =(struct BufferManagement *) ios2->ios2_BufferManagement;
  254.  
  255.     /* Check first if destination hardware address is valid */
  256.     if ((ios2->ios2_DstAddr[0]==0) || (ios2->ios2_DstAddr[0]==255))
  257.     {
  258.         PacketDropped(sdu); /* Can't handle addresses 0 and 255 */
  259.  
  260.         debug(("SendPacket: Bad destination addr:%08lx%04lx (packet dropped).\n",*(LONG *)ios2->ios2_DstAddr,*(UWORD *)(ios2->ios2_DstAddr+4)))
  261.     }
  262.  
  263.     else
  264.     {
  265.       /* Copy the data out of the packet into our temporary buffer. */
  266.       if((*bm->bm_CopyFromBuffer)(sdu->sdu_TxBuff+SHDR_LEN,ios2->ios2_Data,ios2->ios2_DataLength))
  267.       {
  268.         PktHdr=(struct Spar_Hdr *)sdu->sdu_TxBuff; /* Pointer to frame header */
  269.  
  270.         /* Create frame header (Ethernet-like) */
  271.  
  272.         PktHdr->SDstAddr=ios2->ios2_DstAddr[0]; /* Place packet destination Spar address */
  273.  
  274.         /* Place my source address in packet header */
  275.         if ((ios2->ios2_SrcAddr[0]==0) || (ios2->ios2_SrcAddr[0]==255)) /* Valid source address ? */
  276.           PktHdr->SSrcAddr=sdu->sdu_StAddr; /* Use internal h/w addr. */
  277.         else
  278.           PktHdr->SSrcAddr=ios2->ios2_SrcAddr[0];
  279.  
  280.         PktHdr->SFrmType=(UWORD)ios2->ios2_PacketType; /* Place frame type */
  281.  
  282.         /* Update statistics, (without header information) */
  283.         PacketSent(sdu,ios2->ios2_DataLength);
  284.  
  285.         /* Destination address ok, send it */
  286.         iopar = sdu->sdu_ParTx;
  287.         iopar->io_Data = sdu->sdu_TxBuff;
  288.         iopar->io_Length = ios2->ios2_DataLength+SHDR_LEN; /* include packet header */
  289.         iopar->io_Data2 = 0L;             /* must be 0 if you do not */
  290.         iopar->io_Length2 = 0L;           /* use these fields.       */
  291.         iopar->io_Command = CMD_WRITE;
  292.         iopar->io_Error = 0;              /* is this needed?? */
  293.         iopar->io_Message.mn_Node.ln_Type = 0;
  294.         iopar->io_Port = SPAR_PORT;
  295.         iopar->io_Addr = (UWORD)ios2->ios2_DstAddr[0];
  296.         iopar->io_Flags = PRO_DGRAM; 
  297.         /* Send the packet to the PARnet device driver */
  298.         SendIO((struct IORequest *)iopar);
  299.  
  300.         debug(("SendPacket: Sent packet size %ld addr: %08lx%04lx.\n", ios2->ios2_DataLength, *(LONG *)ios2->ios2_DstAddr,*(UWORD *)(ios2->ios2_DstAddr+4)))
  301.       }
  302.       else
  303.       {
  304.         /* Something went wrong...*/
  305.         ios2->ios2_Req.io_Error = S2ERR_NO_RESOURCES;
  306.         ios2->ios2_WireError = S2WERR_BUFF_ERROR;
  307.         DoEvent(sdu,S2EVENT_BUFF);
  308.       }
  309.     }
  310.     TermIO(ios2);
  311. }
  312.  
  313.  
  314. /*
  315. ** This routine is called whenever we think we've got
  316. ** a complete packet to satisfy a CMD_READ request.
  317. */
  318. VOID GotPacket(struct SPARDevUnit *sdu, ULONG length)
  319. {
  320.     struct IOSana2Req *ios2;
  321.     struct BufferManagement *bm;
  322.     ULONG Offset;
  323.     struct Spar_Hdr *PktHdr;  /* Spar frame header */
  324.  
  325.     if(length)
  326.     {
  327.       PacketReceived(sdu,length); /* Update statistics */
  328.  
  329.       PktHdr=(struct Spar_Hdr *)sdu->sdu_RxBuff; /* Pointer to frame header */
  330.  
  331.       /* Find an appropriate request wanting this packet type.
  332.        * Routine gotten from the agnet.device by ppessi <Pekka.Pessi@hut.fi>
  333.        */
  334.       ObtainSemaphore(&sdu->sdu_ListLock);
  335.  
  336.       /* For each queued read request... */
  337.       for (ios2 = (struct IOSana2Req *)sdu->sdu_Rx.mlh_Head;
  338.            ios2->ios2_Req.io_Message.mn_Node.ln_Succ; 
  339.            ios2 = (struct IOSana2Req *)ios2->ios2_Req.io_Message.mn_Node.ln_Succ)
  340.       {
  341.         /* Does requested packet type match? */
  342.         if (ios2->ios2_PacketType == (ULONG)PktHdr->SFrmType)
  343.         {
  344.            Remove((struct Node *)ios2); /* Yes, get & remove it from list */
  345.            break;                       /* No more searches needed */
  346.         }
  347.       }
  348.  
  349.       if(!ios2) /* Nobody wants this packet type? So, it's orphan */
  350.       {
  351.         ReceivedOrphan(sdu);  /* Update statistics */
  352.         if (!(ios2 = (struct IOSana2Req *)RemHead((struct List *)&sdu->sdu_RxOrph)))
  353.           PacketDropped(sdu);  /* Nobody is interested in this packet, drop it */ 
  354.       }
  355.  
  356.       ReleaseSemaphore(&sdu->sdu_ListLock);
  357.  
  358.       bm = (struct BufferManagement *)ios2->ios2_BufferManagement;
  359.  
  360.       /* Don't strip frame header when RAW data is requested */
  361.       if (ios2->ios2_Req.io_Flags==SANA2IOB_RAW) Offset=0;
  362.       else Offset=SHDR_LEN;
  363.  
  364.       /* Copy the data into the protocol stack's buffer using its
  365.        * supplied callback routine. */
  366.       if((*bm->bm_CopyToBuffer)(ios2->ios2_Data,sdu->sdu_RxBuff+Offset,length-Offset))
  367.       {
  368.          debug(("GotPack (%d): %08lx%08lx%08lx%08lx\n",length-Offset,*(LONG *)sdu->sdu_RxBuff,*(LONG *)(sdu->sdu_RxBuff+4),*(LONG *)(sdu->sdu_RxBuff+8),*(LONG *)(sdu->sdu_RxBuff+12)))
  369.  
  370.          /* Copy source and destination addresses */
  371.          memset(ios2->ios2_DstAddr, 0, SANA2_MAX_ADDR_BYTES); /* first clear */
  372.          memset(ios2->ios2_SrcAddr, 0, SANA2_MAX_ADDR_BYTES); /* them both.  */
  373.          ios2->ios2_DstAddr[0]=PktHdr->SDstAddr;
  374.          ios2->ios2_SrcAddr[0]=PktHdr->SSrcAddr;
  375.  
  376.          /* Our packetlength (if not SANA2IOB_RAW then strip frame header) */
  377.          ios2->ios2_DataLength = length-Offset;
  378.  
  379.          /* Our packet type */
  380.          ios2->ios2_PacketType = (ULONG)PktHdr->SFrmType;
  381. #ifdef DEBUG
  382.          debug(("GotPacket: Length:%lx ", ios2->ios2_DataLength))
  383.          if (ios2->ios2_PacketType==ETHERTYPE_ARP)    debug(("Type:ARP "))
  384.          else if(ios2->ios2_PacketType==ETHERTYPE_IP) debug(("Type:IP "))
  385.               else debug(("Type:%08lx ",ios2->ios2_PacketType))
  386.          debug(("Source:%08lx%04lx.\n", *(LONG *)ios2->ios2_SrcAddr,*(UWORD *)(ios2->ios2_SrcAddr+4) ))
  387. #endif
  388.       }
  389.       else
  390.       {
  391.          ios2->ios2_DataLength   = 0;
  392.          ios2->ios2_Req.io_Error = S2ERR_NO_RESOURCES;
  393.          ios2->ios2_WireError    = S2WERR_BUFF_ERROR;
  394.          DoEvent(sdu,S2EVENT_BUFF);
  395.       }
  396.       TermIO(ios2);
  397.     } /* Length */
  398. }
  399.  
  400.  
  401. /*
  402. ** This routine initializes our IO requests and buffers for PARnet i/o .
  403. */
  404. BOOL InitPARnet(struct SPARDevUnit *sdu)
  405. {
  406.     ULONG *clr;
  407.     BOOL status = FALSE;
  408.  
  409.     /* This is a dirty way of clearing some pointers in the struct SPARDevUnit */
  410.     for(clr = (ULONG *) &sdu->sdu_ParRx; clr <= (ULONG *) &sdu->sdu_TxBuff; clr++)
  411.       *clr = 0L;
  412.  
  413.     if(sdu->sdu_TxPort = CreateMsgPort()) /* PARnet CMD_WRITE IORequest reply port */
  414.       if(sdu->sdu_ParTx = CreateIORequest(sdu->sdu_TxPort,sizeof(struct IOParReq)))
  415.           if(sdu->sdu_RxPort = CreateMsgPort()) /* PARnet CMD_READ IORequest reply port */
  416.             if(sdu->sdu_ParRx = CreateIORequest(sdu->sdu_RxPort,sizeof(struct IOParReq)))
  417.                 /* Allocate some buffer memory */
  418.                 if(sdu->sdu_TxBuff = AllocMem(SPAR_MTU * 2,MEMF_CLEAR|MEMF_PUBLIC))
  419.                 {
  420.                   sdu->sdu_RxBuff = sdu->sdu_TxBuff + SPAR_MTU;
  421.                   status = TRUE;  /* flag: initialisation was successful */
  422.                 }
  423.  
  424.     if(!status)
  425.       DeinitPARnet(sdu); /* Something went wrong, release all */
  426.     return(status);
  427. }
  428.  
  429. /*
  430. ** This routine cleans up our PARnet i/o requests
  431. ** and misc. buffers.
  432. */
  433. VOID DeinitPARnet(struct SPARDevUnit *sdu)
  434. {
  435.     if(sdu->sdu_ParTx)
  436.       DeleteIORequest(sdu->sdu_ParTx);
  437.  
  438.     if(sdu->sdu_TxPort)
  439.       DeleteMsgPort(sdu->sdu_TxPort);
  440.  
  441.     if(sdu->sdu_ParRx)
  442.       DeleteIORequest(sdu->sdu_ParRx);
  443.  
  444.     if(sdu->sdu_RxPort)
  445.       DeleteMsgPort(sdu->sdu_RxPort);
  446.  
  447.     if(sdu->sdu_TxBuff)
  448.       FreeMem(sdu->sdu_TxBuff,(SPAR_MTU * 2)); /* release both TxBuff & RxBuff */
  449.  
  450. }
  451.  
  452.  
  453. /*
  454. ** This routine opens the PARnet device driver and attempts to bring
  455. ** the device online.
  456. */
  457. BOOL OpenPARnet(struct SPARDevUnit *sdu)
  458. {
  459.   BOOL status = FALSE;
  460.   ULONG odflags = 0;
  461.   struct IOParReq *ioTpar,*ioRpar; /* IORequest structures */
  462.  
  463.   /* next variables are used for displaying the error message */
  464.   struct EasyStruct es;
  465.   ULONG args[2];
  466.  
  467.   ioRpar = sdu->sdu_ParRx;
  468.   ioTpar = sdu->sdu_ParTx;
  469.   ioRpar->io_Device = NULL;
  470.  
  471.   if (sdu->sdu_TxPort) /* Is this needed?? */
  472.   {
  473.     ioTpar->io_Message.mn_ReplyPort=sdu->sdu_TxPort;
  474.     ioTpar->io_Port  = 0;
  475.     ioTpar->io_Flags = PRO_CONTROL;
  476.  
  477.     if(!OpenDevice(sdu->sdu_ParDevName,sdu->sdu_ParUnitNum,(struct IORequest *)ioTpar,odflags))
  478.     {
  479.       /* Set up our PARnet RX parameters */
  480.       ioRpar->io_Device = ioTpar->io_Device;
  481.       ioRpar->io_Unit   = ioTpar->io_Unit;
  482.  
  483.       /* set up IO request for hardware address setting */
  484.       ioTpar->io_Command = PPD_SETADDR;
  485.       ioTpar->io_Addr    = (UWORD)sdu->sdu_StAddr;
  486.  
  487.       if(!DoIO((struct IORequest *)ioTpar)) /* Set my hardware address */
  488.       {
  489.             /* Close the device again to reset the PRO_CONTROL mode */
  490.             CloseDevice((struct IORequest *)ioTpar);
  491.  
  492.             /* Open the device in Data Gram mode */
  493.             ioTpar->io_Port  = SPAR_PORT;
  494.             ioTpar->io_Flags = PRO_DGRAM;
  495.             if(!OpenDevice(sdu->sdu_ParDevName,sdu->sdu_ParUnitNum,(struct IORequest *)ioTpar,odflags))
  496.             {
  497.                 /* Assume we're now online */
  498.                 sdu->sdu_State |= SPARUF_ONLINE;
  499.  
  500.                 /* check first if the reply port has already been initalized */
  501.                 if (sdu->sdu_RxPort)  /* Is this really needed?? */
  502.                 {
  503.                      /* Queue up the initial CMD_READ command for the PARnet driver. */
  504.                      ioRpar->io_Message.mn_ReplyPort=sdu->sdu_RxPort;
  505.                      ioRpar->io_Port    = SPAR_PORT;  /* PARnet port to listen on */
  506.                      ioRpar->io_Flags   = PRO_DGRAM; /* only DataGram mode supported */
  507.                      ioRpar->io_Command = CMD_READ;
  508.                      ioRpar->io_Length  = SPAR_MTU;
  509.                      if (sdu->sdu_RxBuff)            /* Is this needed?? */
  510.                      {
  511.                           ioRpar->io_Data = sdu->sdu_RxBuff;
  512.                           SendIO((struct IORequest *)ioRpar);
  513.                           status = TRUE; /* All ok! */
  514.                      }
  515.                 }
  516.              }
  517.              if(!status)
  518.                  CloseDevice((struct IORequest *)ioTpar);
  519.       }
  520.       else /* Can't set PARnet hardware address */
  521.         CloseDevice((struct IORequest *)ioTpar);
  522.     }
  523.     else /* Can't open device */
  524.     {
  525.         args[0]=(ULONG)sdu->sdu_ParDevName;
  526.         args[1]=(ULONG)sdu->sdu_ParUnitNum;
  527.  
  528.             es.es_StructSize=sizeof(struct EasyStruct);
  529.             es.es_Flags=0;
  530.             es.es_Title="spar.device";
  531.             es.es_TextFormat="Couldn't open %s unit %ld.";
  532.             es.es_GadgetFormat="Okay";
  533.             EasyRequestArgs(NULL, &es, 0, (APTR)args);
  534.     }
  535.     if(sdu->sdu_State & SPARUF_ONLINE)
  536.       MarkTimeOnline(sdu);
  537.   }
  538.   return(status);
  539. }
  540.  
  541. /*
  542. ** This routine aborts any pending activity with the PARnet
  543. ** device driver and then brings the spar driver offline.
  544. */
  545. VOID ClosePARnet(struct SPARDevUnit *sdu)
  546. {
  547.     AbortIO((struct IORequest *)sdu->sdu_ParRx);
  548.     WaitIO((struct IORequest *)sdu->sdu_ParRx);
  549.  
  550.     while(GetMsg(sdu->sdu_RxPort));
  551.  
  552.     AbortIO((struct IORequest *)sdu->sdu_ParTx);
  553.     WaitIO((struct IORequest *)sdu->sdu_ParTx);
  554.  
  555.     while(GetMsg(sdu->sdu_TxPort));
  556.  
  557.     CloseDevice((struct IORequest *)sdu->sdu_ParRx);
  558.  
  559.     sdu->sdu_State &= ~SPARUF_ONLINE;
  560.  
  561. }
  562.  
  563. /*
  564. ** This routine is called whenever we need to
  565. ** get more data from the PARnet port.
  566. */
  567. VOID QueueParRequest(struct SPARDevUnit *sdu)
  568. {
  569.     sdu->sdu_ParRx->io_Command = CMD_READ;
  570.     sdu->sdu_ParRx->io_Data = sdu->sdu_RxBuff;
  571.     sdu->sdu_ParRx->io_Length = SPAR_MTU;
  572.  
  573.     SendIO((struct IORequest *)sdu->sdu_ParRx);
  574. }
  575.